﻿#ifndef DURON_HTTPCLIENT_H
#define DURON_HTTPCLIENT_H

#include <stdio.h>
#include <string.h>
#include <functional>
#include <memory>
#include "Util/util.h"
#include "Util/mini.h"
#include "Network/TcpClient.h"
#include "Parser.h"
#include "HttpRequestSplitter.h"
#include "HttpCookie.h"
#include "HttpChunkedSplitter.h"
#include "strCoding.h"
#include "HttpBody.h"
using namespace std;


namespace toolkit {

	class HttpArgs : public map<string, variant, StrCaseCompare> {
	public:
		HttpArgs() {}
		virtual ~HttpArgs() {}
		string make() const {
			string ret;
			for (auto &pr : *this) {
				ret.append(pr.first);
				ret.append("=");
				ret.append(strCoding::UrlEncode(pr.second));
				ret.append("&");
			}
			if (ret.size()) {
				ret.pop_back();
			}
			return ret;
		}
	};

	class HttpClient : public TcpClient, public HttpRequestSplitter {
	public:
		typedef StrCaseMap HttpHeader;
		typedef std::shared_ptr<HttpClient> Ptr;
		HttpClient();
		virtual ~HttpClient();
		virtual void sendRequest(const string &url, float fTimeOutSec);

		virtual void clear() {
			_header.clear();
			_body.reset();
			_method.clear();
			_path.clear();
			_parser.Clear();
			_recvedBodySize = 0;
			_totalBodySize = 0;
			_aliveTicker.resetTime();
			_chunkedSplitter.reset();
			HttpRequestSplitter::reset();
		}

		void setMethod(const string &method) {
			_method = method;
		}
		void setHeader(const HttpHeader &header) {
			_header = header;
		}
		HttpClient & addHeader(const string &key, const string &val, bool force = false) {
			if (!force) {
				_header.emplace(key, val);
			}
			else {
				_header[key] = val;
			}
			return *this;
		}
		void setBody(const string &body) {
			_body.reset(new HttpStringBody(body));
		}
		void setBody(const HttpBody::Ptr &body) {
			_body = body;
		}
		const string &responseStatus() const {
			return _parser.Url();
		}
		const HttpHeader &responseHeader() const {
			return _parser.getHeader();
		}
		const Parser& response() const {
			return _parser;
		}

		const string &getUrl() const {
			return _url;
		}
	protected:
		/**
		 * 收到http回复头
		 * @param status 状态码，譬如:200 OK
		 * @param headers http头
		 * @return 返回后续content的长度；-1:后续数据全是content；>=0:固定长度content
		 *          需要指出的是，在http头中带有Content-Length字段时，该返回值无效
		 */
		virtual ssize_t onResponseHeader(const string &status, const HttpHeader &headers) {
			DebugL << status;
			//无Content-Length字段时默认后面全是content
			return -1;
		}

		/**
		 * 收到http conten数据
		 * @param buf 数据指针
		 * @param size 数据大小
		 * @param recvedSize 已收数据大小(包含本次数据大小),当其等于totalSize时将触发onResponseCompleted回调
		 * @param totalSize 总数据大小
		 */
		virtual void onResponseBody(const char *buf, size_t size, size_t recvedSize, size_t totalSize) {
			DebugL << size << " " << recvedSize << " " << totalSize;
		}

		/**
		 * 接收http回复完毕,
		 */
		virtual void onResponseCompleted() {
			DebugL;
		}

		/**
		 * http链接断开回调
		 * @param ex 断开原因
		 */
		virtual void onDisconnect(const SockException &ex) {}

		/**
		 * 重定向事件
		 * @param url 重定向url
		 * @param temporary 是否为临时重定向
		 * @return 是否继续
		 */
		virtual bool onRedirectUrl(const string &url, bool temporary) { return true; };

		//HttpRequestSplitter override
		ssize_t onRecvHeader(const char *data, size_t len) override;
		void onRecvContent(const char *data, size_t len) override;

	protected:
		virtual void onConnect(const SockException &ex) override;
		virtual void onRecv(const Buffer::Ptr &pBuf) override;
		virtual void onErr(const SockException &ex) override;
		virtual void onFlush() override;
		virtual void onManager() override;

	private:
		void onResponseCompleted_l();
		void checkCookie(HttpHeader &headers);

	protected:
		bool _isHttps;

	private:
		string _url;
		HttpHeader _header;
		HttpBody::Ptr _body;
		string _method;
		string _path;
		//recv
		size_t _recvedBodySize;
		ssize_t _totalBodySize;
		Parser _parser;
		string _lastHost;
		Ticker _aliveTicker;
		float _fTimeOutSec = 0;
		std::shared_ptr<HttpChunkedSplitter> _chunkedSplitter;
	};

} /* namespace toolkit */

#endif /* DURON_HTTPCLIENT_H */
